package test;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Random;
import java.util.Set;
import javax.swing.*;
import javax.swing.border.EtchedBorder;


public class Controller extends KeyAdapter implements SnakeListener {
    private Snake snake;
    private Food food;
    private Ground ground;
    private GamePanel gamePanel;
    private Menu menu;
    private BottonPanel bottonPanel;

    public Controller(Snake snake, Food food, Ground ground, Menu menu, GamePanel gamePanel, BottonPanel bottonPanel) {
        this.snake = snake;
        this.food = food;
        this.ground = ground;
        this.gamePanel = gamePanel;
        this.menu = menu;
        this.bottonPanel = bottonPanel;
        init();
    }

    public void init() {
        bottonPanel.getStartButton().addActionListener(new startHandler());
        bottonPanel.getPauseButton().addActionListener(new pauseHandler());
        bottonPanel.getEndButton().addActionListener(new endHandler());

        menu.getitem1().addActionListener(new Item1Handler());
        menu.getitem2().addActionListener(new Item2Handler());
        menu.getitem3().addActionListener(new Item3Handler());
        menu.getitem4().addActionListener(new Item4Handler());

        menu.getSpItem1().addActionListener(new spItem1Handler());
        menu.getSpItem2().addActionListener(new spItem2Handler());
        menu.getSpItem3().addActionListener(new spItem3Handler());
        menu.getSpItem4().addActionListener(new spItem4Handler());

        menu.getMapItem1().addActionListener(new mapItem1Handler());
        //menu.getmapItem2().addActionListener(new mapItem2Handler());
        //menu.getmapItem3().addActionListener(new mapItem3Handler());
        menu.getHelpItem().addActionListener(new helpItemHandler());

        bottonPanel.getStartButton().setEnabled(true);
    }

    /**********获取对象*************/
    public Snake getSnake() {
        return snake;
    }

    public Ground getGround() {
        return ground;
    }

    public GamePanel getGamePanel() {
        return gamePanel;
    }

    public Menu getMenu() {
        return menu;
    }

    public BottonPanel getBottonPanel() {
        return bottonPanel;
    }

    /*********键盘**********/
    @Override
    public void keyPressed(KeyEvent e) {
        switch (e.getKeyCode()) {
        /*case KeyEvent.VK_UP:
            snake.changeDirection(Snake.UP);
            break;
        case KeyEvent.VK_DOWN:
            snake.changeDirection(Snake.DOWN);
            break;
        case KeyEvent.VK_LEFT:
            snake.changeDirection(Snake.LEFT);
            break;
        case KeyEvent.VK_RIGHT:
            snake.changeDirection(Snake.RIGHT);
            break;

        case KeyEvent.VK_W:
            snake.speedUp();
            break;
        case KeyEvent.VK_S:
            snake.speedDown();
            break;
        default:
            break;*/
            case KeyEvent.VK_I:
                snake.changeDirection(Snake.I);
                break;
            case KeyEvent.VK_K:
                snake.changeDirection(Snake.K);
                break;
            case KeyEvent.VK_J:
                snake.changeDirection(Snake.J);
                break;
            case KeyEvent.VK_L:
                snake.changeDirection(Snake.L);
                break;

            case KeyEvent.VK_W:
                snake.speedUp();
                break;
            case KeyEvent.VK_S:
                snake.speedDown();
                break;
            default:
                break;
        }
    }

    @Override
    public void snakeMoved(Snake sanke) {
        gamePanel.display(sanke, food, ground);            //移动更新面板

        if (food.isFoodEated(sanke)) {
            snake.eatFood();
            food.newFood(ground.getPoint());

            bottonPanel.repaint();                        //更新分数板
            setScore();
        }
        if (ground.RockEat(sanke)) {                        //碰到石头
            snake.die();
            bottonPanel.getStartButton().setEnabled(true);
        }
        if (snake.isEatBody()) {                            //碰到身体
            snake.die();
            bottonPanel.getStartButton().setEnabled(true);
        }
    }

    public void setScore() {
        int score = snake.getFoodCount();
        bottonPanel.setScore(score);
    }

    /******开始新游戏***********/
    public void newGame() {
        ground.clear();

        switch (ground.getMapType()) {                //地图类型
            case 1:
                ground.setRocks();
                break;
            case 2:
                ground.setRocks();
                break;
            case 3:
                ground.setRocks();
                break;
        }
        food.newFood(ground.getPoint());
        bottonPanel.setScore(0);

        bottonPanel.repaint();
    }

    /*********游戏监听*********/
    class startHandler implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent arg0) {
            gamePanel.requestFocus(true);
            snake.clear();
            snake.init();
            snake.begin();
            newGame();
            bottonPanel.getStartButton().setEnabled(false);
        }
    }

    /*******暂停键***********/
    class pauseHandler implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            gamePanel.requestFocus(true);
            snake.changePause();

            if (e.getActionCommand() == "暂停游戏")
                bottonPanel.getPauseButton().setText("继续游戏");
            else {
                bottonPanel.getPauseButton().setText("暂停游戏");
            }
        }
    }

    /********结束游戏监听**********/
    class endHandler implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            snake.die();
            bottonPanel.getStartButton().setEnabled(true);
        }
    }

    /********菜单监听***********/
    class Item1Handler implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            Color color = JColorChooser.showDialog(gamePanel, "选择颜色", Color.BLACK);
            gamePanel.backgroundColor = color;
        }
    }

    class Item2Handler implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            Color color = JColorChooser.showDialog(gamePanel, "选择颜色", Color.BLACK);
            food.setFoodColor(color);
        }
    }

    class Item3Handler implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            Color color = JColorChooser.showDialog(gamePanel, "选择颜色", Color.BLACK);
            snake.setHeadColor(color);
        }
    }

    class Item4Handler implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            Color color = JColorChooser.showDialog(gamePanel, "选择颜色", Color.BLACK);
            snake.setBodyColor(color);
        }
    }

    class spItem1Handler implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent arg0) {
            snake.setSleepTime(800);
        }
    }

    class spItem2Handler implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent arg0) {
            snake.setSleepTime(500);
        }
    }

    class spItem3Handler implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent arg0) {
            snake.setSleepTime(180);
        }
    }

    class spItem4Handler implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent arg0) {
            snake.setSleepTime(15);
        }
    }

    class mapItem1Handler implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent argc0) {
            ground.setMapType(1);                ///////////////////////////////////////////////////
        }
    }

    class helpItemHandler implements ActionListener {            ///////////////////////////////
        @Override
        public void actionPerformed(ActionEvent e) {
            StringBuffer sb = new StringBuffer();
            sb.append("方向键控制方向\n");
            sb.append("w++,s--\n");
            String message = sb.toString();
            JOptionPane.showMessageDialog(null, message, "使用说明", JOptionPane.INFORMATION_MESSAGE);
        }
    }
}


class GameFrame extends JFrame {
    public static void main(String[] args) {
        new GameFrame(new Controller(new Snake(), new Food(), new Ground(), new Menu(), new GamePanel(), new BottonPanel()));
    }

    private GamePanel gamePanel;
    private Menu menu;
    private Snake snake;
    private Controller controller;
    private JPanel buttonPanel;

    public GameFrame(Controller c) {
        this.controller = c;
        snake = controller.getSnake();
        menu = controller.getMenu();
        gamePanel = controller.getGamePanel();
        buttonPanel = controller.getBottonPanel();
        setTitle("范盈盈吃老鼠");
        setBounds(300, 100, Global.width * Global.cellsize + 250, Global.height * Global.cellsize + 60);
        setLayout(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Container contentPane = this.getContentPane();
        this.setJMenuBar(menu);
        contentPane.add(gamePanel);
        contentPane.add(buttonPanel);
        setResizable(false);
        setVisible(true);
        /****窗口调整*******/
        //this.setLocation(this.getToolkit().getScreenSize().width /2 - this.getWidth() /3, this.getToolkit().getScreenSize().height - this.getHeight());
        gamePanel.addKeyListener(controller);
        snake.addSnakeListener(controller);
        controller.newGame();
    }
}


interface SnakeListener {
    void snakeMoved(Snake snake);
}


class Food extends Point {
    private static final long serialVersionUID = 1L;
    private Color foodColor;

    public void setFoodColor(Color foodColor) {
        this.foodColor = foodColor;
    }

    public Color getFoodColor() {
        return foodColor;
    }

    public void newFood(Point p) {
        setLocation(p);
    }

    public boolean isFoodEated(Snake snake) {
        return this.equals(snake.getHead());
    }

    public void drawMe(Graphics g) {
        if (foodColor == null) {
            g.setColor(Color.blue);
        } else {
            g.setColor(foodColor);
        }
        g.fill3DRect(x * Global.cellsize, y * Global.cellsize, Global.cellsize, Global.cellsize, true);
    }
}

class Ground {
    private boolean[][] rocks = new boolean[Global.width][Global.height];
    private int mapType = 1;

    /******Map*******/
    public int getMapType() {
        return mapType;
    }

    public void setMapType(int mapType) {
        this.mapType = mapType;
    }

    /*******InitMap*****/
    public void clear() {
        for (int x = 0; x < Global.width; x++) {
            for (int y = 0; y < Global.height; y++) {
                rocks[x][y] = false;
            }
        }
    }

    /******MakeMap******/
    public void setRocks() {
        for (int y = 0; y < 6; y++) {
            rocks[0][y] = true;
            rocks[Global.width - 1][y] = true;
            rocks[0][Global.height - 1 - y] = true;
            rocks[Global.width - 1][Global.height - 1 - y] = true;
        }
    }

    /*****RockEat*********/
    public boolean RockEat(Snake snake) {
        for (int x = 0; x < Global.width; x++) {
            for (int y = 0; y < Global.height; y++) {
                if (rocks[x][y] == true && (x == snake.getHead().x && y == snake.getHead().y)) {
                    return true;
                }
            }
        }
        return false;
    }

    /******SnakeMove*****/
    public Point getPoint() {
        Random random = new Random();
        int x = 0, y = 0;
        do {
            x = random.nextInt(Global.width);
            y = random.nextInt(Global.height);
        }
        while (rocks[x][y] == true);
        return new Point(x, y);
    }

    public void drawMe(Graphics g) {
        g.setColor(Color.gray);                //snake_color

        for (int x = 0; x < Global.width; x++) {
            for (int y = 0; y < Global.height; y++) {
                if (rocks[x][y] == true) {
                    g.fill3DRect(x * Global.cellsize, y * Global.cellsize, Global.cellsize, Global.cellsize, true);
                }
            }
        }
    }
}

class Snake {
    /*****SnakeDirection*********/
    /*public static final int UP = 1;
    public static final int DOWN = -1;
    public static final int LEFT = 2;
    public static final int RIGHT = -2;*/
    public static final int I = 1;
    public static final int K = -1;
    public static final int J = 2;
    public static final int L = -2;
    /*********Listener*********/
    private Set<SnakeListener> listeners = new HashSet<SnakeListener>();
    /*********Point************/
    private LinkedList<Point> body = new LinkedList<Point>();
    /*********SnakeDefinition*********/
    private boolean life;
    private boolean pause;
    private int oldDirection, newDirection;
    private Point oldTail;
    private int foodCount = 0;
    private Color headColor;
    private Color bodyColor;
    private int sleepTime;

    public boolean isLife() {
        return life;
    }

    public int getSleepTime() {
        return sleepTime;
    }

    public void setSleepTime(int sleepTime) {
        this.sleepTime = sleepTime;
    }

    public void setHeadColor(Color headColor) {
        this.headColor = headColor;
    }

    public void setBodyColor(Color bodyColor) {
        this.bodyColor = bodyColor;
    }

    public void init() {
        int x = Global.width / 2;
        int y = Global.height / 2;
        for (int i = 0; i < 3; i++) {
            body.addLast(new Point(x--, y));
        }

        //oldDirection = newDirection = DOWN;                ///////////////////初始方向
        oldDirection = newDirection = I;
        foodCount = 0;
        life = true;
        pause = false;

        if (sleepTime == 0) {
            sleepTime = 1000;                                ////////////////////初始速度
        }
    }

    /*********clearSnake******/
    public void clear() {
        body.clear();
    }

    public void setLife(boolean life) {
        this.life = life;
    }

    public boolean getPause() {
        return pause;
    }

    public void setPause(boolean pause) {
        this.pause = pause;
    }

    /******PauseChange*******/
    public void changePause() {
        pause = !pause;
    }

    /******SnakeDie*********/
    public void die() {
        life = false;
    }

    /*****SnakeMove*********/
    public void move() {
        if (!(oldDirection + newDirection == 0)) {
            oldDirection = newDirection;
        }
        /*****DeleteSnakeTile*****/
        oldTail = body.removeLast();
        int x = body.getFirst().x;
        int y = body.getFirst().y;

        switch (oldDirection) {
        /*case UP:
            y--;
            if (y<0) {
                y = Global.height -1;
            }
            break;
        case DOWN:
            y++;
            if (y>=Global.height) {
                y = 0;
            }
            break;
        case LEFT:
            x--;
            if (x<0) {
                y = Global.width -1;
            }
            break;
        case RIGHT:
            x++;
            if (x>=Global.width) {
                x = 0;
            }
            break;
        }*/
            case I:
                y--;
                if (y < 0) {
                    y = Global.height - 1;
                }
                break;
            case K:
                y++;
                if (y >= Global.height) {
                    y = 0;
                }
                break;
            case J:
                x--;
                if (x < 0) {
                    y = Global.width - 1;
                }
                break;
            case L:
                x++;
                if (x >= Global.width) {
                    x = 0;
                }
                break;
        }

        /*********addSnakeHead*******/
        Point newHead = new Point(x, y);
        body.addFirst(newHead);
    }

    /*********ChangeSnakeDirection********/
    public void changeDirection(int direction) {
        newDirection = direction;
    }

    /**********SnakeEatFood********/
    public void eatFood() {
        body.addLast(oldTail);
        foodCount++;
    }

    public int getFoodCount() {
        return foodCount;
    }

    /**********SnakeEatBody********/
    public boolean isEatBody() {
        for (int i = 1; i < body.size(); i++) {
            if (body.get(i).equals(this.getHead())) {
                return true;
            }
        }
        return false;
    }

    /********GetSankeHead**********/
    public Point getHead() {
        return body.getFirst();
    }

    /********DrawMe****************/
    public void drawMe(Graphics g) {
        if (bodyColor == null) {
            g.setColor(new Color(0x3333FF));
        } else {
            g.setColor(bodyColor);
        }

        for (Point p : body) {
            g.fillRoundRect(p.x * Global.cellsize, p.y * Global.cellsize, Global.cellsize, Global.cellsize, 15, 12);
        }
        drawHead(g);
    }

    /**********DrawSnakeHead*******/
    public void drawHead(Graphics g) {
        if (headColor == null) {
            g.setColor(Color.YELLOW);
        } else {
            g.setColor(headColor);
        }

        g.fillRoundRect(getHead().x * Global.cellsize, getHead().y * Global.cellsize, Global.cellsize, Global.cellsize, 15, 12);
    }

    /********ControlSnakeMove******/
    private class SnakeDriver implements Runnable {
        @Override
        public void run() {
            while (life == true) {
                if (pause == false) {
                    move();
                    for (SnakeListener l : listeners)
                        l.snakeMoved(Snake.this);
                }
                try {
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /****StartThread*******/
    public void begin() {
        new Thread(new SnakeDriver()).start();
    }

    /*****监听******/
    public void addSnakeListener(SnakeListener l) {
        if (l != null) {
            listeners.add(l);
        }
    }

    /******加速*******/
    public void speedUp() {
        if (sleepTime > 50) {
            sleepTime -= 20;
        }
    }

    /******减速*******/
    public void speedDown() {
        if (sleepTime < 700) {
            sleepTime += 20;
        }
    }


}

class Global {
    public static final int cellsize = 18;
    public static final int width = 25;
    public static final int height = 25;
}

class BottonPanel extends JPanel {
    private static final long serialVersiomUID = 1L;

    private JButton startButton;
    private JButton pauseButton;
    private JButton endButton;
    private JLabel scoreLabel;
    private int score;

    public BottonPanel() {
        setLayout(null);
        setBounds(455, 0, 235, 450);

        startButton = new JButton("开始游戏");
        startButton.setBounds(10, 20, 100, 25);
        add(startButton);

        pauseButton = new JButton("暂停游戏");
        pauseButton.setBounds(10, 60, 100, 25);
        add(pauseButton);

        endButton = new JButton("结束游戏");
        endButton.setBounds(10, 100, 100, 25);
        add(endButton);
        /*
        scoreLabel = new JLabel("得分：");
        scoreLabel.setFont(new Font("Serif",Font.BOLD,18));
        scoreLabel.setBounds(30, 140, 100, 30);
        add(scoreLabel);*/

        Color c = new Color(0, 250, 154);
        this.setBackground(c);
        this.setFocusable(true);
    }

    public JButton getStartButton() {
        return startButton;
    }

    public JButton getPauseButton() {
        return pauseButton;
    }

    public JButton getEndButton() {
        return endButton;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.PINK);
        g.setFont(new Font("Serif", Font.BOLD, 50));
        g.drawString(score + "", 40, 230);
    }


}

class GamePanel extends JPanel {
    private static final long seriaVersionUID = 1L;

    private Snake snake;
    private Food food;
    private Ground ground;
    public Color backgroundColor;

    public GamePanel() {
        setLocation(0, 0);
        /*****大小、布局*******/
        this.setSize(Global.width * Global.cellsize, Global.height * Global.cellsize);
        this.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
        this.setFocusable(true);
    }

    public void display(Snake snake, Food food, Ground ground) {
        this.snake = snake;
        this.food = food;
        this.ground = ground;
        repaint();
    }

    public void clearDraw(Graphics g) {
        if (backgroundColor == null) {
            g.setColor(new Color(0xacaf));                            /***********************默认背景颜色*****************************/
        } else {
            g.setColor(backgroundColor);
        }
        g.fillRect(0, 0, Global.width * Global.cellsize, Global.height * Global.cellsize);
    }

    @Override
    public void paint(Graphics g) {
        clearDraw(g);
        if (ground != null && snake != null && food != null) {
            ground.drawMe(g);
            food.drawMe(g);
            snake.drawMe(g);
        }
        if (snake != null && snake.isLife() == false) {
            recover(g);
        }
    }

    /******恢复***********/
    public void recover(Graphics g) {
        clearDraw(g);
        /******game over*******/
        g.setColor(Color.gray);                                        /**********************默认失败颜色**************************/
        g.setFont(new Font("Serif", Font.BOLD, 50));
        g.drawString("Game Over!", 130, 210);
    }
}


class Menu extends JMenuBar {
    private static final long serivalVersionUID = 1L;
    private JMenu colorMenu, speeceMenu, mapMenu, helpMenu;
    private JMenuItem item1, item2, item3, item4;
    private JMenuItem spItem1, spItem2, spItem3, spItem4;
    private JMenuItem mapItem1;//待更新,mapItem2,mapItem3;
    private JMenuItem helpItem;

    /****************颜色********/
    public JMenu getColorMenu() {
        return colorMenu;
    }

    public JMenu getSpeeceMenu() {
        return speeceMenu;
    }

    public JMenu getMapMenu() {
        return mapMenu;
    }

    @Override
    public JMenu getHelpMenu() {
        return helpMenu;
    }

    /*****************2***************/
    public JMenuItem getitem1() {
        return item1;
    }

    public JMenuItem getitem2() {
        return item2;
    }

    public JMenuItem getitem3() {
        return item3;
    }

    public JMenuItem getitem4() {
        return item4;
    }

    /******************速度***************/
    public JMenuItem getSpItem1() {
        return spItem1;
    }

    public JMenuItem getSpItem2() {
        return spItem2;
    }

    public JMenuItem getSpItem3() {
        return spItem3;
    }

    public JMenuItem getSpItem4() {
        return spItem4;
    }

    /*****************地图*****************/
    public JMenuItem getMapItem1() {
        return mapItem1;
    } /*	public JMenuItem getmapItem2() {	//待更新return mapItem2; } public JMenuItem getmapItem3() { return mapItem3; } */

    /*****************帮助*******************/
    public JMenuItem getHelpItem() {
        return helpItem;
    }

    /*****************Menu*************/
    public Menu() {
        colorMenu = new JMenu("颜色");    //color
        add(colorMenu);
        speeceMenu = new JMenu("速度");    //speece
        add(speeceMenu);
        mapMenu = new JMenu("地图");    //map
        add(mapMenu);
        helpMenu = new JMenu("帮助");    //help
        add(helpMenu);
        item1 = new JMenuItem("背景");
        item2 = new JMenuItem("食物");
        item3 = new JMenuItem("蛇首");
        item4 = new JMenuItem("蛇身");
        colorMenu.add(item1);
        colorMenu.add(item2);
        colorMenu.add(item3);
        colorMenu.add(item4);
        spItem1 = new JMenuItem("匍匐");
        spItem2 = new JMenuItem("静步");
        spItem3 = new JMenuItem("巡逻");
        spItem4 = new JMenuItem("乌拉！");
        speeceMenu.add(spItem1);
        speeceMenu.add(spItem2);
        speeceMenu.add(spItem3);
        speeceMenu.add(spItem4);
        mapItem1 = new JMenuItem("宿舍楼"); /*item2 = new JMenuItem("食物"); item3 = new JMenuItem("蛇首"); item4 = new JMenuItem("蛇身"); colorMenu.add(item1); colorMenu.add(item2); colorMenu.add(item3);*/
        mapMenu.add(mapItem1);
        helpItem = new JMenuItem(".帮助");
        helpMenu.add(helpItem);
    }
}